﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using Elentok.Common.Helpers;

namespace Elentok.Common
{
  /// <summary>
  /// This class allows to save/load presets
  /// </summary>
  public static class Presets
  {
    /// <summary>
    /// The key of the presets directory item in the app.config's appSettings section
    /// </summary>
    public const string PresetsDirectoryConfigKey = "PresetsDirectory";

    /// <summary>
    /// Gets the directory in which the presets will be saved
    /// </summary>
    public static string PresetsDirectory { get; private set; }

    static Presets()
    {
      #region Set the presets directory
      if (ConfigurationManager.AppSettings[PresetsDirectoryConfigKey] != null)
        PresetsDirectory = ConfigurationManager.AppSettings[PresetsDirectoryConfigKey];
      else
        PresetsDirectory = Path.Combine(SystemHelper.GetUserSettingsPath(true), "Presets");

      if (!Directory.Exists(PresetsDirectory))
        Directory.CreateDirectory(PresetsDirectory);
      #endregion
    }

    /// <summary>
    /// Returns the full path of a preset file (<see cref="PresetsDirectory"/> + "\\" + <paramref name="name"/>)
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static string GetPresetFullPath(string name)
    {
      return Path.Combine(PresetsDirectory, name);
    }

    /// <summary>
    /// Loads the preset from file (<see cref="PresetsDirectory"/> + "\\" + <paramref name="name"/>)
    /// </summary>
    /// <typeparam name="TPreset"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    public static TPreset Load<TPreset>(string name) where TPreset : IPreset, new()
    {
      TPreset preset = new TPreset();
      using (FileStream stream = File.OpenRead(GetPresetFullPath(name)))
      {
        preset.Load(stream);
      }
      return preset;
    }

    /// <summary>
    /// Saves <paramref name="preset"/> to file (<see cref="PresetsDirectory"/> + "\\" + <paramref name="name"/>)
    /// </summary>
    /// <param name="preset"></param>
    /// <param name="name"></param>
    public static void Save(IPreset preset, string name)
    {
      using (FileStream stream = File.OpenWrite(GetPresetFullPath(name)))
      {
        preset.Save(stream);
      }
    }

    /// <summary>
    /// Loads the preset from file (<see cref="PresetsDirectory"/> + "\\" + <paramref name="name"/>)
    /// (uses the <see cref="XmlSerializer"/> to deserialize the preset object)
    /// </summary>
    /// <typeparam name="TPreset"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    public static TPreset LoadFromXml<TPreset>(string name)
      where TPreset : class
    {
      TPreset preset = default(TPreset);
      using (FileStream stream = File.OpenRead(GetPresetFullPath(name)))
      {
        XmlSerializer serializer = new XmlSerializer(typeof(TPreset));
        preset = (TPreset)serializer.Deserialize(stream);
      }
      return preset;
    }

    /// <summary>
    /// Saves the preset to file (<see cref="PresetsDirectory"/> + "\\" + <paramref name="name"/>)
    /// (uses the <see cref="XmlSerializer"/> to serialize the preset object)
    /// </summary>
    /// <param name="preset"></param>
    /// <param name="name"></param>
    public static void SaveToXml(object preset, string name)
    {
      using (XmlTextWriter writer = new XmlTextWriter(GetPresetFullPath(name), Encoding.UTF8))
      {
        writer.Formatting = Formatting.Indented;
        XmlSerializer serializer = new XmlSerializer(preset.GetType());
        serializer.Serialize(writer, preset);
      }
    }

    /// <summary>
    /// Returns true if a preset by the name "<paramref name="name"/>" exists.
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public static bool Exists(string name)
    {
      return File.Exists(GetPresetFullPath(name));
    }

    /// <summary>
    /// Returns the names of all of the available presets
    /// </summary>
    /// <returns></returns>
    public static ICollection<string> GetAllPresets()
    {
      List<string> presetNames = new List<string>();
      DirectoryInfo dir = new DirectoryInfo(PresetsDirectory);
      foreach (FileInfo file in dir.GetFiles())
        presetNames.Add(file.Name);
      return presetNames;
    }

    /// <summary>
    /// Returns the names of all of the available presets that match the search pattern
    /// </summary>
    /// <param name="searchPattern"></param>
    /// <returns></returns>
    public static ICollection<string> GetAllPresets(string searchPattern)
    {
      List<string> presetNames = new List<string>();
      DirectoryInfo dir = new DirectoryInfo(PresetsDirectory);
      foreach (FileInfo file in dir.GetFiles(searchPattern))
        presetNames.Add(file.Name);
      return presetNames;
    }
  }
  
  /// <summary>
  /// This interface defines the base methods required for implementing a preset
  /// </summary>
  public interface IPreset
  {
    /// <summary>
    /// Saves the preset to <paramref name="stream"/> (can be in any format you want)
    /// </summary>
    /// <param name="stream"></param>
    void Save(Stream stream);

    /// <summary>
    /// Loads the preset from the <paramref name="stream"/> (must match the format in which the preset was saved).
    /// </summary>
    /// <param name="stream"></param>
    void Load(Stream stream);
  }
}
